﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// 国际化的通用框架, 使用示例(webapi):
/// <example>
/// <code>
/// var builder = WebApplication.CreateBuilder(args);
/// //先注册国际化kv
/// I18N.RegisterLanguage("zh", new Dictionary&lt;string, string> { { "name", "杰克" } });
/// I18N.RegisterLanguage("en", new Dictionary&lt;string, string> { { "name", "jack" } });
/// 
/// var app = builder.Build();
/// //使用中间件拦截请求,设置当前使用的 language
/// app.Use(async (ctx, next) =>
/// {
///     if (string.Equals(ctx.Request.Query["lang"], "zh", StringComparison.OrdinalIgnoreCase))
///     {
///         I18N.SetCurrentLanguage("zh");
///     }
///     else
///     {
///         I18N.SetCurrentLanguage("en");
///     }
///     await next();
/// });
/// app.MapGet("/", () =>
/// {
///     return I18N.GetString("name");
/// });
/// app.Run();
///
/// //"http://localhost:5270/?lang=zh": 后台服务返回: "杰克"
/// //"http://localhost:5270/?lang=en": 后台服务返回: "jack"
/// </code>
/// </example>
/// </summary>
public static class I18N
{
    private const string I18N_LANGUAGE = "I18N_LANGUAGE";

    #region Register...
    private static readonly Dictionary<string, Dictionary<string, string>> languageStrings = [];
    private static Dictionary<string, string> defaultLanguageStrings = [];
    /// <summary>
    /// 注册语言的kv映射(可以指定是否作为默认的语言):
    /// <code>
    /// I18N.RegisterLanguage("zh", new Dictionary&lt;string, string> { { "name", "杰克" } });
    /// </code>
    /// </summary>
    public static void RegisterLanguage(string language, Dictionary<string, string> kvs, bool asDefault = false)
    {
        AssertUtil.NotNull(kvs);
        languageStrings[language] = kvs;
        if (asDefault) defaultLanguageStrings = kvs;
    }
    private static Func<string, string> fallBackFunc = null;
    /// <summary>
    /// 注册当找不到语言的kv映射时的处理逻辑, 如: 没有注册语言 "zh-CN" 或 没有在语言 "zh-CN" 中找到 "name" 的映射, 使用示例:
    /// <code>
    /// I18N.RegisterFallBack(key => $"cannot find {key}.");
    /// </code>
    /// </summary>
    public static void RegisterFallBack(Func<string, string> func)
    {
        fallBackFunc = func;
    }
    private static Func<string, Task<string>> fallBackFuncAsync = null;
    /// <summary>
    /// 注册当找不到语言的kv映射时的处理逻辑, 如: 没有注册语言 "zh-CN" 或 没有在语言 "zh-CN" 中找到 "name" 的映射, 使用示例:
    /// <code>
    /// I18N.RegisterFallBack(key => $"cannot find {key}.");
    /// </code>
    /// </summary>
    public static void RegisterFallBack(Func<string, Task<string>> func)
    {
        fallBackFuncAsync = func;
    }
    #endregion

    #region CurrentLanguage
    /// <summary>
    /// 设置当前运行环境的语言, 也可以使用 <seealso cref="RunInLanguage(string, Action)"/>
    /// </summary>
    public static void SetCurrentLanguage(string language)
    {
        RunningContext.Current.SetValue(I18N_LANGUAGE, language);
    }

    /// <summary>
    /// 获取当前运行环境设置的语言, 如果还没有设置则抛出异常
    /// </summary>
    public static string GetCurrentLanguage()
    {
        return RunningContext.Current.GetValue<string>(I18N_LANGUAGE);
    }

    /// <summary>
    /// 判断当前环境是否设置了语言
    /// </summary>
    public static bool HasSetLanguage()
    {
        return RunningContext.Current.ContainsKey(I18N_LANGUAGE);
    }

    /// <summary>
    /// 尝试获取当前环境设置的语言
    /// </summary>
    public static bool TryGetLanguage(out string lang)
    {
        return RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out lang);
    }
    #endregion    

    #region RunInLanguag
    /// <summary>
    /// 使 func 下的代码都运行在 language 环境中, 如:
    /// <code>
    /// I18N.RunInLanguage("zh-CN", () =>
    /// {
    ///     //some code
    /// });
    /// </code>
    /// </summary>
    public static async Task RunInLanguageAsync(string language, Func<Task> func)
    {
        await RunningContext.RunInContextAsync(async () =>
        {
            SetCurrentLanguage(language);
            await func();
        });
    }

    /// <summary>
    /// 使 func 下的代码都运行在 language 环境中, 如:
    /// <code>
    /// I18N.RunInLanguage("zh-CN", () =>
    /// {
    ///     //some code
    /// });
    /// </code>
    /// </summary>
    public static void RunInLanguage(string language, Action func)
    {
        RunningContext.RunInContext(() =>
        {
            SetCurrentLanguage(language);
            func();
        });
    }
    #endregion

    #region GetString
    /// <summary>
    /// 获取当前语言下的 key 的映射
    /// </summary>
    public static string GetString(string key)
    {
        if (RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out var language))
        {
            if (languageStrings.TryGetValue(language, out var languageDic))
            {
                if (languageDic.TryGetValue(key, out var value)) return value;
            }
        }
        if (defaultLanguageStrings != null && defaultLanguageStrings.TryGetValue(key, out var value2)) return value2;
        if (fallBackFunc != null) return fallBackFunc(key);
        if (fallBackFuncAsync != null) return fallBackFuncAsync(key).Result;
        return null;
    }

    /// <summary>
    /// 获取当前语言下的 key 的映射
    /// </summary>
    public static async Task<string> GetStringAsync(string key)
    {
        if (RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out var language))
        {
            if (languageStrings.TryGetValue(language, out var languageDic))
            {
                if (languageDic.TryGetValue(key, out var value)) return value;
            }
        }
        if (defaultLanguageStrings != null && defaultLanguageStrings.TryGetValue(key, out var value2)) return value2;
        if (fallBackFuncAsync != null) return await fallBackFuncAsync(key);
        if (fallBackFunc != null) return fallBackFunc(key);
        return null;
    }
    #endregion
}
